perm filename MBOX.DGL[UP,DOC]19 blob
sn#408694 filedate 1979-01-10 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00031 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00004 00002 Abstract:
C00006 00003 Basic idea:
C00012 00004 SYS:MBOX.
C00018 00005 Multiple calls on the same instrument:
C00031 00006 Scanner Declarations :
C00036 00007 Predeclared user settable conditions variables:
C00040 00008 Predeclared variables preset by MUSBOX:
C00042 00009 Allocation of processing elements at scan level.
C00044 00010 Reserved procedures in MUSBOX:
C00047 00011 Preloaded variables:
C00048 00012 Symbolic constants:
C00050 00013 Misc. things:
C00053 00014 The scanner-parser:
C00054 00015 Filename handler and conventions:
C00060 00016 Interactive mode:
C00062 00017 Setting timing variables for Samson box output:
C00065 00018 What each instrument is handed by the scanner
C00072 00019 Things available to instrumens from MUSBOX.
C00074 00020 BNF of MUSBOX
C00084 00021 New style SAMINS file with sample instruments, modular organization
C00100 00022 ∂ Concept of organization
C00102 00023 ∂ What SAMINS looks like
C00106 00024 ∂ Alternate form for samins
C00107 00025 ∂ What an instrument file looks like
C00109 00026 ∂ SIMP - This instrument is a simple oscillator.
C00112 00027 ∂ MixMod - Multiply two signals by a constant factors between 0 and 8.
C00115 00028 ∂ Summary
C00118 00029 ∂ Loading additional load_modules from SAMINS.
C00133 00030 Putting up the system, conditional compilation and loading:
C00137 00031 That's about all you need to know. If you have any questions,
C00138 ENDMK
C⊗;
Abstract:
This describes the program MUSBOX, written by Gareth Loy,
which is an interpreter-compiler for generating commands for the
Systems Concepts Digital Synthesizer. It is written in the SAIL
programming language and makes extensive use of the parallel process
runtimes available in that language so as to develop a precise and
powerful simulation of the highly pipelined and multiplexed environment
of the Systems Concepts Synthesizer.
At SUAI the system version of this program is SYS:MBOX.
The input language is made to be compatable (insofar as is reasonable)
with the various software sound compilers such as MUSCMP, MUSIC5, etc.
But the facilities available to the sophisticated user goes beyond this
by making the entire SAIL language accessabe to the instrument designer,
such that this program allows ultimate expansion out to the full capabilities
of SAIL.
More documentation is available on various aspects of the Samson box system:
@SUAI:
LRNSAM.DGL[UP,DOC] Operating manual and tutorial
LOWER.TXT[DOC,MUS] Description of command assembler
INTERM.TXT[DOC,MUS] Intermediate level SAIL routines
SAMINS.DOC[INS,MUS] Documentation of available instruments on [INS,MUS]
Basic idea:
The basic operation of this program is as follows:
MUSBOX reads an event list called a PLAY block which is prepared by
the user and which has the role of a musical score. The basic
function of this list is to invoke the execution of special procedures
called instruments. Instruments receive the values passed to them from
the MUSBOX scanner when they are executed, and they interpret the data
passed to them to compile a set of commands to configure the synthesizer.
This stream of commands is written out to the disk. Various ways
exist for passing the disk file to the synthesizer and of hearing the result.
There are two forms of use of this program:
1) novices use a prepared dump of the program available on the system
which is preloaded with a
fixed selection of instruments designed for didactic purposes.
These configure the synthesizer to perform a variety of simple
functions such as sinusoid generation, FM, filtering, reverb, etc.
(For a description of these instruments, see SAMINS.DOC[INS,MUS]. For
a simple description of how to use them, read the page in this
file headed "SYS:MBOX.")
2) Advanced users "roll their own", making their own collection
of instruments from a broader instrument library, or creating their own
instruments. Even there, a large library of basic functions is available
to the designer, which follow standard conventions allowing arbitrary
combinations. (The documentation for other available instruments can also be
found in SAMINS.DOC[INS,MUS]. To create a version of MUSBOX that contains
your menu of instruments, see later in this file under "New Style SAMINS."
No consistent documentation exists that describes how to design
your own instruments. However, there are examples of instruments later
in this file called "Simp" and "MixMod." In addition, the area [INS,MUS]
contains many instruments that can be used as models.)
The design criteria for the construction of this program has been
to make available to the user the broadest possible programming environment
for the configuration of the synthesizer, while trying to make the
inner workings of the program as transparent as possible. There is
a long way to go in the full realization of these criteria. For instance,
the design of instruments is somewhat complicated in comparison
to the equivalent task in the various software sound compilers such as
MUSCMP, NEWMUS, MUSIC5, etc. Several reasons for this are apparent:
For one thing we are controlling a much more complicated process here
than is the case in software compilers. Also, an attempt has been made
to make as much of the total capabilities of SAIL available to the designer
of instruments whereas no known software compiler (and very few other
programming languages) have the flexibility (and hence the complexity) of
this approach. The usage of SAIL in this program touches almost every
component of it including its dynamic core allocation, parallel processing
capabilities, record structure and procedure variables. The low level
routines are hand coded in FAIL, the local assembly language.
The system is sufficiently modular to allow many customized packages.
The problem of designing instruments
is still under development, and great progress is expected in easing the
task of the instrument designer. However it is already about 95% easier than
it was in the beginning and there is every reason to expect it to continue
to improve.
SYS:MBOX.
To run this program type to the monitor:
.R MBOX
This dump of MUSBOX is preloaded with a set of instruments that can be
invoked to generate a file of commands that can be passed to the Synthesizer
to configure it to play something. To get a full description of these
instruments, see SAMINS.DOC[INS,MUS]. The first instrument is described
below as well as there.
The simplest instrument is called SIMP. It is capable of
generating one note where the pitch, amplitude, begin time and duration
are controlled by parameters passed to it in a specific sequence.
Here's an example of an input file: (Reserved words are capitalized.)
PLAY;
SIMP 0 .5 A 1 F1;
FINISH;
PLAY caused MUSBOX to begin looking for instruments to run.
You say PLAY when you want MUSBOX to start writing out commands, so it opens
an output file. It uses the default output filename of TEST.SAM. If you
wish to call the output file something else, put the name after PLAY (e.g.
PLAY FOO; will cause the file FOO.SAM to be written).
SIMP is an instrument
predefined in the system version of MUSBOX and the numbers and symbols after it
occupy fields that are scanned and passed to SIMP. They are called P fields and
the values they are given determine what SIMP will play.
To refer to a particular P field, attach a number to it:
For instance, P1 is 0, P2 is .5, P3 is A, etc. The meanings of these fields
are relative to what the instrument expects to get, which is defined by
the instrument's design. There are a couple of P fields that have specific
meanings, however: P0 is always an instrument name, in this case SIMP.
P1 is always a begin time in seconds,
P2 is always a duration in seconds. Beyond that, conventional practice
makes P3 be a center frequency (in Hertz) and P4 an amplitude (usually arbirarily
scaled to go from 0 to 1). Thus in the example, SIMP will start sounding
at 0 seconds after the beginning of the piece, will sound for .5 seconds,
at a pitch of A=440 Hz, at an overall amplitude of 1 (loud).
The next P field is F1 which is a predefined function of time
vs. amplitude which is
appropriate for use as the amplitude envelope of the note. This function
is a piecewise linear description of the instaneous amplitude through time
of our note. Users can create their own functions, see below.
The FINISH statement forces all instruments not yet
run to do so, prints out some scheduling information having to do with
the running of the instruments, and prompts for more input from the user.
To hear the sound that we have created, if MBOX says
"Input file:" first type the <RETURN> key. MBOX will give you the prompt ">".
Now type <control>P (that is, hold down the <control> key and type P).
MBOX now asks you for number of times to play.
Hit <RETURN> again to play it once. To stop, type <ALT>.
You can create your own functions, called SEG functions. These functions
can be created using either Leland Smith's FUNC program, or
the program SYS:TFUN written by Andy Morrer. (If you
don't know about TFUN, some documentation is available in INTERM.TXT[DOC,MUS],
under the subject of FUNED and EDFUN, there is also some help available
in the program itself, .R TFUN and type HELP.)
You can create longer scores either making them up with the editor,
or with Leland Smith's SCORE program (get LCS to tell you about this).
Multiple calls on the same instrument:
Unlike MUSCMP (that is, NEWMUS and MUSIC5 and friends) instruments in MUSBOX
are capable of running more than one copy of themselves at a time.
When MUSBOX runs an instrument, it instantiates a copy of the values
passed to that instrument for each time the instrument is called.
In essence, the code for each instrument is a "template" of the actual
instrument that is run. In this way, if you want an instrument to play
two notes at the same time, the program SPROUTS two copies of this template,
keeping the values passed to the instruments from the PLAY block seperate
for each instantiation or sprout of the instrument template.
In short, overlapping the same instrument with itself will work (up to the
limit of the Samson box hardware).
Scanner Declarations :
------------------------------------------------------------------------
VARIABLE takes a string of names as arguement, delimited by spaces
and/or commas, which MUSBOX will treat as REALs. (Everything in
MUSBOX is handled as a REAL and only fixed when necessary.) These
variables are stored in a parallel array system,
and can be used in any expression context such as
assignment statements,etc.
INSTRUMENT takes a template name as its first argument then any number
of other unique identifiers which will be associated with
this template;
COMMON same as INSTRUMENT, but someday INSTRUMENT will be redefined
to cause MUSBOX to look up a .REL file full of instrument code
and read it in. At that time COMMON will be the only way
to associate a template with a series of other identifiers
that you want to also point to that template;
PLAY begins a PLAY block. May be followed with an optional argument
specifying an output file, which will have the default extension
".DOA" for SAMSON box output or ".PLA" for FRM output. No
filename defaultts to "TEST" plus extension. Or, instead
of a filename you can say "ASK" and MUSBOX will prompt the user
at execution time and get the filename that way.
Entering a PLAY block initializes lots of things as described
below;
It is legal to say PLAY inside a PLAY block (i.e., PLAY-FINISH
do not really constitute a block boundary pair). This does the
same thing as resetting the pass counter to 0 (see below). In
this way it is possible to concatenate play lists.
FINISH Ends a PLAY block, forces all instruments to quit, closes
the output file. An optional argument after the FINISH statement
specifies the number of seconds to wait after the end of the
last instrument before turning off the dacs and doing a reset;
RECORD takes a filename with default extension ".FUN" and uses
a special handler to read in SEG functions. Knows about "ASK";
INSERT takes a filename with default extension ".SAM", suspends
execution of current level, jumps to the new file and
continues execution there, returning to this level when
that is exausted. Files can be inserted up to 15 deep.
.FUN files can also be read in this way by adding the
explicit extension. Knows about "ASK";
EXIT forces MUSBOX to quit and returns you to the monitor, after
closing all files, and runs SAMX with the output filename.
DONT_SCAN stop scanning statements until you see:
SCAN which turns scanning back on. This is useful for making
MUSBOX ignore part of the score.
DELETE_RECORDS wipes out the list of record functions previously entered
with RECORD or FUNC.
Predeclared user settable conditions variables:
The following variables are tested by MUSBOX when it scanns a PLAY
statement to set overall conditions. To have any effect, therefore, they
must be set before issuing the PLAY statement.
------------------------------------------------------------------------
nptix assign to how many processing ticks, defaults to 96;
nutix assign to how many update ticks, defaults to 32;
ttix total of the above two, this determines the sampling rate;
srate will contain sampling rate calculated from nptix and nutix;
mag will have frequency scaling calculated from srate;
pass this is the pass counter, used to determine where we are in
the flow of time. PRINT PASS; tells you where you are in
the command stream. It is a writable variable, but as such
is reserved for wizards.
noutchans assign between 0 and 4 output channels,
defaults to quad;
whichside assign 0 for dacs to read from generator sum memory only,
positive for modifier side only, negative for both sides,
defaults to negative;
filters for low pass filters on the dacs, assign any of these values:
"unfiltered" ≡ bypass analog filters,
0 ≡ 4.5 kHz
1 ≡ 9.0 kHz
2 ≡ 13.5 kHz
3 ≡ 18.0 kHz,
defaults to 3;
packing set to left_justified, right_justified or full_word,
defaults to full_word which is interpreted by LOWER to
only load full_word when necessary;
optimization set to OPTIMIZE or NON_OPTIMIZE to determine whether
LOWER will pack more commands per word where possible,
defaults to OPTIMIZE;
debug assign with any summation of
debug_instruments to see what values they are passed,
debug_scanning to see what is read in from a file,
debug_arithmetic to see how MUSBOX does it's arithmetic,
debug_stack to see the contents of arithmetic stacks,
debug_scheduling to see how MUSBOX sprouts instruments;
debug_symbols tells about symbol scanner;
boxtyp not used. If you have another synthesizer that you wish to
use MUSBOX on, this can be used to select the appropriate
command assembly routines.
Predeclared variables preset by MUSBOX:
All of the following are set up when you issue a PLAY statement.
They will not be assigned actual values until then! These are writable
variables, so if you want to clobber them you can. No guarantees, however
that doing so will get you anywhere.
------------------------------------------------------------------------
ZERO predeclared sum memory location to always return zero;
That is, since sum memory clears itself on every pass,
if nothing is ever written into a location, that location
will be 0. The location ZERO is reserved by convention,
as the place we never write into (although we could and
boy, would that wreck havoc!) so that we always get 0 from it.
OUTA, OUTB...D predeclared sum memory locations to stuff generator output;
OUTMA, OUTMB..D predeclared sum memory locations to stuff modifier output;
Allocation of processing elements at scan level.
NOTE: These claim and return the address of the next free sum memory
location. They require initialization induced by the PLAY statement, and their
results are invalid outside a PLAY block. Don't try to claim processing
elements outside a PLAY block, the results will be invalid.
------------------------------------------------------------------------
GEN_SUM returns the next free last pass sum memory location;
MOD_SUM likewise for modifiers;
GET calls LOWER routine GET with two arguments, the processing
element id, and the relative number of the processing element.
If the second argument is TRUE then GET returns the lowest
order free processing element. Example: A←Get(id_modifier,63)
claims and returns the address of the 63'rd modifier.
GETDM takes length of delay memory desired as argument,
returns base address if successful in claiming memory,
else returns -1. Example A←GetDm(1000) claims 1000 words
of delay memory and returns the base address of the block
in A;
Reserved procedures in MUSBOX:
------------------------------------------------------------------------
ABS returns the absolute value of the argument.
INT returns the integer part of the argument.
LOG returns the log base e of the argument.
SIN returns the sine of the argument.
PRINT prints the subsequent arguments on the user's screen.
PRINT TAKES NO PARENTHESES AROUND THE ARGUMENT LIST.
Example: Print "HI THERE",2*3 b←a, 2*(a+1),↓;
prints HI THERE 6.0 440.0 882.0 <cr>.
NOTE: string constants must be delimited by commas, otherwise
RDNUM returns a real number from the user's terminal;
RDARG like rdnum, it returns a real number from the user's terminal
but if the user types <cr> instead of a number, then the
expression passed to rdarg is returned. For instance,
A←rdarg(3*(foo+1)) will either assign A the number
typed by the user, or, if the user types <cr>, (or if the
user types something that is not a valid real number, like
.oo1 for instance) then it
will assign A the value of 3*(foo+1). Please note, that
the only acceptable input from the user terminal is a
REAL NUMBER, not an expression, but that the argument to
rdarg can be any expression (including calls to other
procedures, etc.);
RAND returns a random number;
WAITER causes the main process (MBOX) to suspend itself until
the time in passes secified by the argument. The main use
of this is to put a silence between subsequent PLAY blocks:
PLAY; SIMP <args>; FINISH; PLAY; WAITER(3*SRATE); FINISH;
PLAY; SIMP <args>; ... will insert 3 seconds of silence
between the first PLAY block and the last one;
Preloaded variables:
These variables are set up when MUSBOX is entered and are valid
anywhere. They are assigned the values indicated below them.
------------------------------------------------------------------------
A,AS,B,C,CS,D,DS,E,F,FS,G,GS,
440,466.16,493.89,261.62, 277.18,293.66,311.13,329.63,349.23,369.99,391.99,415.31,
BF,CF,BS,DF,EF,FF,ES,GF,AF
466.16,246.945,523.24,277.18,311.13,329.63,349.23,369.99,415.31
Symbolic constants:
These are predeclared symbols. They contain a value and may not
be overwritten, but the value may be referenced (e.g. a←true, but not true←a).
-------------------------------------------------------------------------
Generators:
g_inactive, g_pause, a_running, b_running, g_wait, c_running,
data_read, data_write, dac_write,
lplusq, lminusq, lexpplus, lexpminus,
sum_of_cosines, sawtooth, square, pulse_train, sine, sin_fm;
Modifiers:
m_inactive, u_noise, tr_u_noise, latch, threshold, invoke_delay_unit,
notwopoles, two_0poles, two_1poles,
notwozeroes, two_0zeroes, two_1zeroes,
int_mixing, one_pole, mixing, one_zero,
four_quad_multiply, am,
maximum, minimum, signum, zero_crossing_pulser,
add_sum_memory, replace_sum_memory;
Delay units:
delay,
d_inactive,
delayline, table_lookup, round_table_lookup;
Debugging:
debug_instruments, debug_arithmetic, debug_scheduling, debug_scanning,
debug_stack,debug_records,debug_template;
Initialization:
samdev,frmdev,
unfiltered,
optimize,non_optimize,
right_justified,left_justified,full_word;
Misc. commands:
pause_clear,wait_clear;
Constants:
pi,
true,false;
Misc. things:
------------------------------------------------------------------------
COMMENT can be used to enter comments in the text, everything scanned
until the first semicolon is seen will magically disappear;
<, ∂ these are also comment charactors and cause MUSBOX to flush the
entire input line. It does not break on a semicolon, but flushes
everything until it sees a <crlf>. SO DON'T PUT ANY STATEMENTS YOU
WANT TO HAVE EXECUTED ON THE SAME LINE AS "<" or "∂";
' When this charactor is scanned at the beginning of a number
the immediately subsequent value is presumed to be in octal.
↓ This charactor cause a <cr><lf> to be printed on the user's terminal;
ASK When supplied as an argument anywhere that a filename is expected,
MUSBOX will prompt the user for a filename instead of taking
one from where the ASK statement was found.
See the description of the filename scanner.
(e.g. PLAY ASK; at the head of a PLAY block
will cause the filename handler to prompt the user for
an output file (with .SAM extension implicit). RECORD ASK;
will cause the filename handler to prompt the user for a function
file (with .FUN implicit)).
INSTRUMENTS
is the token referring to the list of loaded instruments. When
used in conjunction with PRINT, it will print the list of those
instruments: PRINT INSTRUMENTS;
FUNCTIONS
is the token for the list of loaded SEG, SYNTH and SSEG functions
e.g.: PRINT FUNCTIONS prints the list;
PNS is the token for the list of last referenced P fields.
PRINT PNS; causes as many P fields as were most recently addressed
by the last instrument scanned. Thirty two P fields are printed
if no instrument has yet been run.
The scanner-parser:
MUSBOX has an inverse Polish expression scanner that works in
the usual fashion.
For instance, in the expression 1 + 2*3↑3, the result will be 55.
The expression (1+2)*3↑(3-1) will be 27.
Precedence of operators is:
[")"][unary negation] (e.g., "-1", "1-1" is binary negation)
["↑"] exponentiation
["*"]["/"]["⊗"] ("⊗"=left shift, e.g. 1 ⊗ 2 = 4)
["+"]["-"]
["("]["←"] ("←" = assignment statement)
Operators on the same line are of equal precedence.
Multiple operations of the same precedence are done left to right,
e.g., 2+3-4+5 = 6.
Filename handler and conventions:
MUSBOX has one filename handler for all the different kinds
of files it reads. In all the modes that follow, it is possible to
put in any number of filenames (seperated by a comma or
space, or whatever) and they will be acted on in turn.
Partial filename specifications are always allowed.
Depending on the context of the filename request, certain
file extensions are defaultable.
There are three levels of input to MUSBOX: FILE PROMPT level,
TTY INPUT level and FILE INPUT level
FILE PROMPT level is what MUSBOX does when it says
"Input file:".
If the <filename> extension is not supplied, MUSBOX looks for a sequence
of files with the following extensions: .HED, .SCR, .PLA, .BOX. If you want
to input a file that has no extension, you must say <filename>.. (that's
right, a period after the filename).
If a filename is found, a lookup is immediately done and command switches
to the contents of that file.
If <alt> is typed, then MUSBOX exits back to the monitor.
If <return> (also called <cr>) is typed, MUSBOX goes to TTY PROMPT level.
FILE input level is when MUSBOX is snarfing down a file that
you pointed it to. This level and TTY level are handled identically,
so whatever you say in a file you can also type in at a TTY and vice
versa (exept for Interactive Mode commands, see below).
When the file is exausted, you are returned to FILE PROMPT level.
TTY INPUT level prompts with a ">". As far as MUSBOX is concerned,
this input mode and FILE INPUT level are identical (exept as noted).
To return to FILE PROMPT level, type <alt>. (The next <alt> would exit MUSBOX.)
There are, however some special commands available at TTY level. For
a discussion, see the page on Interactive Mode.
Files can be read in at other than at FILE PROMPT level.
The following commands work for either TTY or FILE INPUT level.
INSERT can be used to switch to another file for further processing
of commands. Execution of the new file begins immediately. More than
one file can be specified, and they are taken in turn. The INSERTed
file can in turn do an INSERT, and these can nest to the level of 15 deep.
It has the same filename extension defaults as FILE PROMPT level.
RECORD input statement accepts the default .FUN extension, otherwise
it behaves exactly like the other input modes. This .FUN extension is the
usual one put on files containing SEG or SYNTH functions.
(Leland C. Smith memorial feature #1) In addition the term FUNC
behaves exactly like RECORD.
The PLAY statement takes an optional argument for the name
of the command output file. Default filename is TEST, default extension
is .SAM.
In addition, until the time that a PLAY statement is scanned, you
can say (Leland C. Smith memorial feature #2) OUTPUT <filename> (defaults
the same as PLAY).
For the filename argument of all of the above (INSERT, RECORD, FUNC,
PLAY and OUTPUT) you can substitute the term ASK. In this case, MUSBOX
will prompt the user for a filename instead, keeping the default extensions
appropriate for the command. For example, a file containing the statement
INSERT ASK; would cause MUSBOX to type out "Insert file: " and wait for the
user to type a filename. Execution would then switch to this file.
NOTE: MUSBOX has a seperate scanner for function files (things
with .FUN extension). This scanner is usually invoked by saying
RECORD <filename> <filename>...;
This scanner can be invoked from FILE PROMPT level
by explicitly writing the filename extension of the first file.
For example, these are the same:
>RECORD FOO BAZ BAR;
and
Input File: FOO.FUN BAZ BAR
Interactive mode:
The following commands at TTY level have these effects:
In all versions of MUSBOX:
<filename>αβε Edit the named file. If no filename, then edit
the last file opened by MUSBOX. If no file opened,
then edit the last file ever edited.
<filename>αβP Play the named file through the Samson box.
Asks for # times to play, wait, type <alt> to abort.
This runs Tovar's SAMPLA program as a phantom job to
actually run the synthesizer.
In MUSBOXE (SYS:MBOXE at SUAI)
<function>αβE Edit function with EdFun. Type HELP after entering
EdFun for a list of commands. EdFun is the same routine
that is in SYS:TFUN.
<filename>αβW Write out functions to named file. (TEST.FUN default)
Setting timing variables for Samson box output:
You can now set any of SRATE, MAG, TTIX (total ticks), NPTIX
(number of processing ticks) or NUTIX (number of update ticks) and
the remainder of these variables will be set to reasonable values
(it sez here). Hence, you can say SRATE←25600; and the right things
get done to accomplish that. However, do not try to reset any of
these variables inside a play block, as unpredictable things
will happen.
Additional warnings: The "right thing" may not always be
unambiguous, and MUSBOX does what it can. For instance, simply
setting TTIX does not specify the division between NPTIX and NUTIX,
so this is defaulted to a rather arbitrary ratio between the two: 3/1.
This ambiguity can be overcome by setting NPTIX and NUTIX
yourself, but letting MUSBOX do it should be ok most of the time.
Also, since the thing that really determines the sampling rate
is the integer TTIX, setting SRATE (which is a floating point number)
will instead give you the sampling rate
calculated from the nearest integer value of TTIX. The same goes for MAG.
Everything else is set from this also. So if the SRATE you get is
not exactly the SRATE you set, that's why.
Processing ticks are restricted to the range ≥2 and ≤256.
Update ticks are restricted to be ≥2.
Total ticks are always 8 more than the sum of update and processing
ticks (to account for overhead ticks).
The numbers actually compiled into the SAM command stream
will be slightly different, reflecting how the box likes to think
about these things, that is:
Highest tick will be TTIX-2. This is because the counter must
start at 0, and be one less than the number required (see the Synthesizer
specifications).
Highest proc. tick will be NPTIX-1 also because counter must start
at 0, again as per the spec. sheet.
What each instrument is handed by the scanner
A record pointer named Pns is passed to each instrument. It points
to a record which contains all the fields that the scanner
scanned for this instantiation of the instrument. It includes the
following:
Three parallel arrays, one real, one of type record_pointer(any_class)
and one of type string.
These arrays contain all the fields that the scanner found for this instrument
from P0 out to the current maximum of P128. The real array contains
the values parsed from arithmetic expressions, the record_pointer array
contains record pointers to the SEG functions to be used by the instrument
for such things as amplitude and frequency control through time.
The string array is special, and contains the string representation of
the field as it was scanned by MBOX. There are a couple of special things
to mention about the string array. If you don't need to know
these gory details, skip the rest of this paragraph.
1) If a field is made up of more than
one symbol or number, and there are spaces, tabs, etc. between the symbols
or numbers, these are suppressed in the string array. For instance,
if a P field contained the following: 1 + 2 * A, then the string array
element will contain a packed version of it: 1+2*A.
2) Lower case is converted to upper case.
3) If a string constant
is to be passed as a distinct field, it must be delimited by commas. A string
constant is a string with quotes around it. For instance, in the statement:
PRINT "ONE = ",1;, the "ONE = " is a string constant. What I am saying is, that
if you want to pass something like this to an instrument:
INS 0 1 A 1 F1 "MUMBLE", OUTA;, then "MUMBLE" must have a comma after it.
(Why? I'm glad you asked! It's because, just like in SAIL, undelimited
strings can precede ANY token. For a SAIL example: BEGIN "FOO" INTEGER A;...
The "FOO" is an undelimited string constant, (also the name of the block).
The undelimited string constant must be suppressed, and is, and it's the
same in MBOX. The statement "MUMBLE" VARIABLE BAZ; is treated exactly
the same as the statement VARIABLE BAZ. However "MUMBLE",VARIABLE BAZ;
is taken as two seperate fields. Got it?)
Here's an example of a PLAY list:
if the scanner saw a PLAY list that looked exactly like this:
PLAY;
SIMP 0 1 A amp1 1 + 2 + 3 OUTA,"mumble","foo";
FINISH;
(where SIMP is the name of an instrument, A is a predeclared variable
that returns the value 440,
OUTA is a predeclared variable
containing the addresses of a sum memory location,
and finally, AMP1 is a SEG function that has been read in from a file),
then the fields presented to the instrument
on the three parallel arrays will be as follows:
array element number:
0 1 2 3 4 5 6 7 8
value in string array:
"SIMP" "0" "1" "A" "AMP1" "1+2+3" "OUTA" "MUMBLE" "FOO"
value in real array:
debug 0. 1. 440. 0. 6. 384. 0 0
value in record_pointer array:
SIMP nr nr nr AMP1 nr nr nr nr
We see that only one SEG record was passed to this instrument in P4
(nr is used here to mean "null_record", i.e., no record is being pointed to).
A record_pointer to SIMP shows up in P0 of the record_pointer array.
Remember that instruments are named in P0, so this field points to the instrument
descriptor record. You can find out useful things about the instrument
from this, but I won't go into details.
On the real array, we see the result of 1+2+3 and the address of
sum memory location OUTA (384).
In the real array, the meaning of debug is this: since P0 is
reserved for the record_pointer to the instrument linked list, no number
can occupy P0. So instead, a debug flag is placed there which the instrument
can interpret. When a user says something like
DEBUG←DEBUG_INSTRUMENTS;
to the scanner, the scanner turns on a certain bit in the debug flag corresponding
to the value represented by the symbolic constant DEBUG_INSTRUMENTS.
This flag is copied into the real array when the instrument is called.
An instrument can subsequently test to see if this bit is on, and if it is,
can then do various things, such as print out to a teletype the values
it was passed so the user can see if they agree with what he thinks should
be there. For an example of this, see the sample instruments Simp and MixMod
later in this file.
Things available to instrumens from MUSBOX.
external procedure BoxError(string errmsg);
Invokes MBOX's error handler with your message.
external boolean procedure getRecord(string name;
reference record_pointer(any_class) r;
record_pointer(any_class) Tops);
Searches a linked list for a record name matching the string name.
Searches the list pointed to by Top, returns the pointer in r.
external procedure LinkUp(
reference record_pointer(any_class) lstTop;
Record_Pointer(any_class) lstTmpTop;
integer lstTyp(functionList));
This is used to link the record named in lstTmpTop
to the list named in lstTop.
external record_pointer(InsCls) insTop;
Points to the top of the instrument linked list.
external record_pointer(InsCls) funTop;
Points to the top of the function linked list.
External real srate,mag;
Has the value of the sampling rate and mag.
BNF of MUSBOX
Take this with a grain of salt...
<play_block> ::= PLAY; <play_list> |
PLAY <filename>; <play_list>
<play_list> ::= <instrument_call>;<play_list>|
<statement_list>;<play_list>|
FINISH
<statement_list>::= <statement_list><statement>
<statement> ::=<null> | <reserved_word> | <field_list> |
<instrument_call> | <procedure_call>
<instrument_call>::= <instrument_identifier> |
<instrument_identifier><field_list>
<procedure_call>::= <procedure_identifier> |
<procedure_identifier> ( <field_list> )
<field_list> ::= <field> | <field_list><field_mark><field>
<field> ::= <expression> | <record_pointer> | <instrument_identifier> |
<procedure_call> | <delimited_string_constant>
<field_mark> ::= <space> | <comma> | <tab> | <cr> | <ff> | <lf>
New style SAMINS file with sample instruments, modular organization;
∂ This is the example file for the modular approach to instrument
design. See the last page of this section for a summary;
∂ This description is oriented towards users who need to convert from the
old style format. If you don't know what that was, you're better off,
so read on and don't worry about it.
∂ Where the time savings comes from:
∂ A special dump of the SAIL compiler called INSCOM is used to compile the
instruments which has it's symbol tables preloaded with SAMLIB.HDR, the code that
used to be needed on page 2 of SAMINS and lots of other useful stuff;
The time it takes to load SAIL's symbol table constitutes most of the time
it takes to compile an instrument.
∂ The instruments are put into seperate files of their own, and compiled
seperately from eachother. This means that you never need to recompile an
instrument that already works just because it's in a file with another instrument
that does not. Any collection of these instruments can be assembled even
though they are in seperate files;
∂ I estimate that I've saved about 90% of the time it used to take to recompile
instrument files, and having only one copy of the source code for the instrument
saves lots of disk space;
∂ Concept of organization;
∂ Basicly it's simple. A SAMINS file used to contain three categories
of code:
1) Code on page 2 contained macros and requires for the following instruments.
2) The instruments.
3) A procedure (usually called something like MakeInsList) that called
LnkIns or LnkInsP.
The new format simply rearranges this to be more flexible, smaller chuncks;
∂ Each instrument is now in a file of its own. Each instrument has a companion
procedure to link it up to MBOX. (Many instruments can be in one file, but
each still has an individual link procedure so they can be split easily);
∂ There is still a SAMINS file, but it's only function is to tell MBOX which
instruments to load;
∂ What SAMINS looks like;
∂ The code that used to be on page 2 is already inside INSCOM, so is not needed;
∂ There is no BEGIN "INSTS" statement here because it has been put inside
INSCOM also;
∂ All the declarations in SAMLIB.HDR[SUB,SYS] as well as
all the macros in SAMINS.HDR[SAM,MUS] are already
available to be used here, including LnkIns, Parameters, InsLnk, etc.;
∂ However,
the END "INSTS" statement must be at the end of all code in SAMINS.SAI
In addition, it must be at the end of every seperate file that you want
INSCOM to compile, i.e., all your instruments as well. If you want to have
more than one instrument in a file, then put the end "INSTS"; at the end
of the last instrument;
∂ Since each instrument has its own procedure to link it to MBOX, SAMINS does
not need to, so its only job is to require the .REL files that contain
the compiled instruments;
∂ Here is the only unfortunate aspect of this whole mess:
To use INSCOM you must have a small file,
INSCOM.REL[SAM,MUS] in your directory (it's 380 some words long). This
file is copied out by INSCOM and expanded upon to become SAMINS.REL which you
load with MBOX. Say:
.COPY INSCOM.REL[SAM,MUS] to get this file on your area;
∂ What follows is a complete SAMINS.SAI file to load instruments SIMP and
MIXMOD. Code for these two instruments can be found on the next pages,
but would presumably be in seperate files of their own;
∂ Here begins SAMINS.SAI;
require "SIMP.REL[SAM,DGL]" load_module;
require "MIXMOD.REL[SAM,DGL]" load_module;
end "INSTS";
∂ Here ends SAMINS.SAI;
∂ It is necessary for this END statement to be here, see above;
∂ It is also necessary that "INSTS" be CAPITALIZED!;
∂ This is the end of SAMINS.SAI;
∂ Alternate form for samins;
∂ if all the instrument .REL files are on one area from which MBOX will
be loaded, then the following predefined macro can be used;
∂ The source code of this macro is in SAMINS.HDR[SAM,MUS];
∂ Here begins SAMINS:
Instruments(<(simp,mixmod)>);
end "INSTS";
∂ Here ends SAMINS:
∂ and that's all there is to it!;
∂ What an instrument file looks like;
∂ Again, none of the stuff that used to be on page 2 is required;
∂ No BEGIN "INSTS" is needed, but END "INSTS" is necessary;
∂ In outline form, here is a simple instrument:
(file begins here)
Internal recursive procedure InstrumentName(parameters);
begin "InstrumentName"
.
.
.
end "InstrumentName";
InsLnk(<InstrumentName,HowManyParameters>);
end "INSTS";
(file ends here)
∂ Esential differences to old instrument are:
1) Must add INTERNAL to the type of the procedure.
2) Each instrument must call InsLnk to link it to MBOX;
∂ InsLnk takes the following forms:
1) InsLnk(<InstrumentName,HowManyParameters>)
2) InsLnk(<InstrumentName>).
In case (2) the instrument will get 32 parameters by default.
∂ More than one instrument can be in a file if you wish, but each one
must be followed by its own call to InsLnk;
∂ SIMP - This instrument is a simple oscillator.;
∂ Parameters:
P1 P2 P3 P4 P5 P6
Beg Dur Freq Amp Ampfun outloc
;
Internal recursive procedure Simp(Parameters);
begin
real Freq,Amp;
integer outloc, Beg, Dur;
record_pointer(seg)AmpFun; ∂ For storing locations of function;
itemvar AmpItem;
integer Gena,DFlag;
resetP(0); ∂ resetP, incP and pCtr are three macros that simplify ;
DFlag ← Pn[incP]; ∂ picking up parameters. ResetP resets the value of pCtr ;
Beg ← Pn[incP]; ∂ to the value of its argument (in this case 0). IncP ;
Dur ← Pn[incP]; ∂ returns the value of pCtr, then increases its value by 1.;
Freq ← Pn[incP]; ∂ This allows the quantity and order of parameters to be ;
Amp ← Pn[incP]; ∂ reshuffled with impunity. Also, pCtr-1 is the highest ;
AmpFun ← Pf[incP]; ∂ parameter referenced by the instrument, and can be passed;
outloc ← Pn[incP]; ∂ to InsLnk. They are defined in SAMINS.HDR[SAM,MUS];
deleteArray; ∂ Deletes parameter array;
if AmpFun=null_record then BoxError("SIMP: No amp. function!");
if Decode(outLoc)≠id_sum_memory then outLoc←outa;
if DFlag=debug_instruments then
PRINT(↓,"SIMP ",cvn(myproc)," P1 = ",Beg/srate," P2 = ",Dur/srate,↓);
Gena ← GOSC(Beg, Dur, Freq,
AmpFun, amp, 0, AmpItem,
SineMode, 0, zero, outloc);
Join({AmpItem});
RelPe(Gena);
RelItm(myproc);
end;
InsLnk(<simp,pCtr-1>); ∂ Links this instrument to MBOX;
END "INSTS"; ∂ Must be last thing in instrument file;
∂ MixMod - Multiply two signals by a constant factors between 0 and 8.
∂ sample call:
MixMod beg dur factor1 factor2 in1loc in2loc outloc;
Internal recursive procedure MIXMOD(Record_Pointer(InsArrRec) Pns);
begin
real factor1, factor2;
integer in1Loc, in2Loc, outLoc, beg, dur;
integer mod, debIns;
resetP(0);
debIns ←Pn[incP];
beg ←Pn[incP];
dur ←Pn[incP];
factor1 ←Pn[incP];
factor2 ←Pn[incP];
in1Loc ←Pn[incP];
in2Loc ←Pn[incP];
outLoc ←Pn[incP];
$RECFN(5,Pns); ∂ delete global Pns array record pointer;
if debIns=debug_instruments
then
print("MIXMOD",cvn(myproc), " P1= ",beg/srate, "P2= ",dur/srate,↓,
tab, factor1, factor2, sp, in1Loc, sp, in2Loc,sp, outLoc,↓);
mod ← MIX(outLoc,in1Loc,factor1,
in2Loc,factor2);
Waiter(beg+dur);
bind(mod,mode,m_inactive);
RelPe(mod);
if debIns=debug_instruments
then
print("MIXMOD: ",cvn(myproc), " P1= ",beg/srate, "P2= ", dur/srate,
" Dropping off end",↓);
RelItm(myproc);
end;
InsLnk(<mixmod,pCtr-1>);
end "INSTS";
∂ end of MIXMOD;
∂ Summary;
Advantages to modular approach;
1) no need for more than one copy of the source code for an instrument.
2) eliminates needles recompilation of instruments that work to debug
ones that don't.
3) Saves about 90% compile time in comparsion to running the straight
SAIL compiler because the symbol tables are already loaded.
A) INSTRUMENTS:
1) make each instrument a seperate file.
2) forget about the formerly everpresent, ubiquitous page 2 code.
3) end the instrument with end "INSTS"; (CAPITALIZED!)
4) there is no corresponding begin "INSTS", that's inside INSCOM.
5) R INSCOM for each instrument file to be compiled.
6) Say <FILENAME> ← <FILENAME> to INSCOM. It will read
<FILENAME>.SAI and write <FILENAME>.REL.
My method is to make the .SAI filename the same as the .REL one.
Also, I make the instrument name the same as the filename, just for convenience.
7) when you have done this for every instrument you want to load then
make up a SAMINS file that will load them all together.
B) SAMINS:
1) require the instrument files as load_modules.
2) end all your instrument files with end "INSTS" (CAPITALIZED).
3) R INSCOM and say SAMINS←SAMINS. Same thing happens as with
instruments.
C) Now, load MBOX as you ordinarily would:
.LOAD MBOX[SAM,MUS]/SAI/REL/SAV
That's it!
D) Now, when you need to recompile an instrument, you only need compile
that instrument (using INSCOM), then load MBOX again. If you want to change
the instruments which are loaded, you must change SAMINS,
compile it (with INSCOM) and reload MBOX.
∂ Loading additional load_modules from SAMINS.
Please note, that because of the order in which load_modules are required
in MBOX, there is the facility that you can have your instrument file, SAMINS.REL
require any load_modules, and those will be found and loaded by the LOADER
before SAMLIB is looked up. This means, for instance, that if you were
debugging a piece of code from SAMLIB, you could have a same-named procedure
on your own area, required in your SAMINS file, and when the LOADER is doing
it's thing it will load your piece of code and not the one from SAMLIB, simply
because it loads the first thing it finds, and subsequent same-named pieces
of code are ignored.
(Note, this is not the same problem as having two Internal or External
declarations in your source code. That is a multiply defined symbol. But
having two .REL files with the same procedure is ok, the LOADER just finds
the first one and ignores the second.)
NOTE: For some reason, the LOADER doesn't load LIBRARIES in the
order that they are REQUIRED. If you have a library that you want to load
that will require things from other libraries (like SAMLIB for instance)
it is necessary to explicitly run the loader on that file first. For example:
.LOA MYLIB,MBOX
will successfully load MYLIB even if requiring it as a LIBRARY does not.
;
Putting up the system, conditional compilation and loading:
The current version of MUSBOX is always kept as MBOX.SAI[SAM,MUS].
There are conditional compilation switches documented on page 2 of that
file which select whether the version will have EdFun, whether it will
be able to call SAMPLA, etc. That information is duplicated below.
∂ To compile the standard version of MBOX, comment out XSAMLB and WITH_EDFUN;
∂ To compile version with the function editor EDFUN and SSWRT,
remove the comment from WITH_EDFUN and
tell the system: .COMPILE MBOXE=MBOX, which will create MBOXE.REL;
∂ To compile the version that requires the experimental library,
XSAMLB.REL[SUB,SYS], remove comment from XSAMLB and .COMPILE XMBOX=MBOX
which will create XMBOX.REL;
∂ SAMCAL should be in all versions;
∂ To get your own version of MBOX, after you have made an instrument
file called SAMINS.REL on your area (see MBOX.DGL[UP,DOC] for how
to do this) then type to the monitor
.LOAD MBOX[SAM,MUS]/SAI/SAV/REL;
∂ To put up the system version of MBOX, alias to [INS,MUS] and
.LOAD MBOX[SAM,MUS]/SAI/SAV/REL for the regular verson or
.LOAD MBOXE[SAM,MUS]/SAI/SAV/REL for the version with EdFun or
.LOAD MBOX[SAM,MUS]/SAI/SAV/REL for the version that requires XSAMLB;
LOADing MBOX from [INS,MUS] causes MBOX to require SAMINS.REL[INS,MUS]
which contains pointers to the instruments for the system version
When the LOADER is done, .ALIAS 1,3 (which is where system programs
are kept) and .RENAME MBOX.DMP[INS,MUS]. Alias back to your area and
it should be possible to .R MBOX (or MBOXE or whatever) and run
the newly created dump;
Following are the various compile time switches for MUSBOX.
They consist of macros, which if are defined, will have the effect
described after their DEFINE. If the DEFINE is commented out, then
that switch will not be set (It does NOT work to change them to FALSE);
COMMENT DEFINE WITH_EDFUN=TRUE; ∂ load with EDFUN and SSWRT;
COMMENT DEFINE XSAMLB=TRUE; ∂ Require XSAMLIB.REL[SUB,SYS];
DEFINE WITH_SAMCAL=TRUE; ∂ enables αβP to run TVR's SAMPHA;
That's about all you need to know. If you have any questions,
I just stepped out...